package com.kinsin.kinsinlogsystem;

import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class ZipManager {
    private static final String TAG = "ZipManager";
    private static long MAX_PART_SIZE = 45_000_000L;
    private static final int BUFFER_SIZE = 128 * 1024;

    private final Handler mainHandler;
    private ZipTask currentTask;
    private ZipProgressListener externalListener;

    public ZipManager() {
        this.mainHandler = new Handler(Looper.getMainLooper());
    }

    public void startCompression(List<File> sourceFiles, String baseName, long max_part_size,
                                ZipProgressListener listener) {
        if (max_part_size > 0) {
            MAX_PART_SIZE = max_part_size;
        }
        cancel();
        this.externalListener = listener;
        currentTask = new ZipTask(sourceFiles, baseName, new InternalListener());
        currentTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }

    public void cancel() {
        if (currentTask != null && !currentTask.isCancelled()) {
            currentTask.cancel(true);
            currentTask.cleanup();
            notifyCancel();
        }
    }

    private class InternalListener implements ZipProgressListener {
        @Override
        public void onProgress(final int percent) {
            mainHandler.post(new Runnable() {
                @Override
                public void run() {
                    if (externalListener != null) {
                        externalListener.onProgress(percent);
                    }
                }
            });
        }

        @Override
        public void onComplete(final List<File> zipFiles) {
            mainHandler.post(new Runnable() {
                @Override
                public void run() {
                    if (externalListener != null) {
                        externalListener.onComplete(zipFiles);
                    }
                }
            });
        }

        @Override
        public void onError(final Throwable throwable) {
            mainHandler.post(new Runnable() {
                @Override
                public void run() {
                    if (externalListener != null) {
                        externalListener.onError(throwable);
                    }
                }
            });
        }

        @Override
        public void onCancel() {
            mainHandler.post(new Runnable() {
                @Override
                public void run() {
                    if (externalListener != null) {
                        externalListener.onCancel();
                    }
                }
            });
        }
    }

    private void notifyCancel() {
        mainHandler.post(new Runnable() {
            @Override
            public void run() {
                if (externalListener != null) {
                    externalListener.onCancel();
                }
            }
        });
    }

    private static class ZipTask extends AsyncTask<Void, Integer, List<File>> {
        private final List<File> sourceFiles;
        private final String baseName;
        private final ZipProgressListener internalListener;
        private final AtomicBoolean isCancelled = new AtomicBoolean(false);
        private final long totalBytes;
        private long processedBytes;
        private ZipOutputStream zos;
        private FileOutputStream fos;
        private BufferedOutputStream bos;
        private File currentPart;
        private int currentPartNumber = 1;

        public ZipTask(List<File> sourceFiles, String baseName, ZipProgressListener listener) {
            this.sourceFiles = new ArrayList<File>(sourceFiles);
            this.baseName = baseName;
            this.internalListener = listener;
            this.totalBytes = calculateTotalSize();
        }

        private long calculateTotalSize() {
            long total = 0;
            for (File file : sourceFiles) {
                total += file.length();
            }
            return total;
        }

        @Override
        protected List<File> doInBackground(Void... params) {
            List<File> result = new ArrayList<File>();
            try {
                result = compressFiles();
            } catch (IOException e) {
                if (!isCancelled.get()) {
                    internalListener.onError(e);
                }
            }
            return result;
        }

        private List<File> compressFiles() throws IOException {
            List<File> outputFiles = new ArrayList<File>();
            FileOutputStream fos = null;
            BufferedOutputStream bos = null;

            try {
                for (int i = 0; i < sourceFiles.size(); i++) {
                    if (isCancelled.get()) break;

                    File file = sourceFiles.get(i);
                    checkNewPart(outputFiles);

                    // 添加文件到压缩流
                    ZipEntry entry = new ZipEntry(file.getName());
                    zos.putNextEntry(entry);

                    FileInputStream fis = null;
                    try {
                        fis = new FileInputStream(file);
                        byte[] buffer = new byte[BUFFER_SIZE];
                        int bytesRead;
                        while ((bytesRead = fis.read(buffer)) != -1) {
                            if (isCancelled.get()) break;
                            zos.write(buffer, 0, bytesRead);
                        }
                    } finally {
                        if (fis != null) {
                            try {
                                fis.close();
                            } catch (IOException e) {
                                Log.e(TAG, "关闭文件输入流失败", e);
                            }
                        }
                    }

                    zos.closeEntry();
                    processedBytes += file.length();

                    // 更新进度
                    int progress = (int) ((processedBytes * 100) / totalBytes);
                    publishProgress(progress);
                }
            } finally {
                closeCurrentStream();
            }
            return outputFiles;
        }

        private void checkNewPart(List<File> outputFiles) throws IOException {
            if (currentPart == null || currentPart.length() >= MAX_PART_SIZE) {
                closeCurrentStream();

                currentPart = createPartFile(baseName, currentPartNumber++);
                outputFiles.add(currentPart);

                fos = new FileOutputStream(currentPart);
                bos = new BufferedOutputStream(fos, BUFFER_SIZE);
                zos = new ZipOutputStream(bos);
            }
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            if (values != null && values.length > 0) {
                internalListener.onProgress(values[0]);
            }
        }

        @Override
        protected void onPostExecute(List<File> files) {
            if (!isCancelled.get()) {
                internalListener.onComplete(files);
            }
        }

        @Override
        protected void onCancelled() {
            isCancelled.set(true);
            internalListener.onCancel();
        }

        void cleanup() {
            closeCurrentStream();
            deletePartialFiles();
        }

        private void closeCurrentStream() {
            if (zos != null) {
                try {
                    zos.flush();
                    zos.close();
                } catch (IOException e) {
                    Log.e(TAG, "关闭压缩流失败", e);
                }
            }
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    Log.e(TAG, "关闭缓冲流失败", e);
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    Log.e(TAG, "关闭文件输出流失败", e);
                }
            }
            // 避免重复关闭或操作已释放的资源
            zos = null;
            bos = null;
            fos = null;
        }

        private void deletePartialFiles() {
            if (currentPart != null && currentPart.exists()) {
                currentPart.delete();
            }
        }

        private File createPartFile(String base, int partNumber) {
            return new File(base + ".part" + String.format("%03d", partNumber) + ".zip");
        }
    }

    public static void validateZipParts(final List<File> waitValidateFiles, final List<File> originals, final ValidationCallback callback) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                List<String> errors = new ArrayList<String>();
                Map<String, Long> crcMap = new HashMap<String, Long>();
                // 进度计算参数
                int currentStep = 0;

                // 计算原始文件CRC（占进度95%）
                try {
                    for (File file : originals) {
                        CRC32 crc = new CRC32();
                        FileInputStream fis = new FileInputStream(file);
                        byte[] buffer = new byte[BUFFER_SIZE];

                        int bytesRead;
                        while ((bytesRead = fis.read(buffer)) != -1) {
                            crc.update(buffer, 0, bytesRead);
                        }
                        fis.close();
                        crcMap.put(file.getName(), crc.getValue());

                        // 更新进度
                        currentStep++;
                        if (callback != null) {
                            callback.onValidationProgress((currentStep * 95) / originals.size());
                        }
                    }
                } catch (IOException e) {
                    errors.add("CRC计算失败: " + e.getMessage());
                    if (callback != null) {
                        callback.onValidationComplete(errors);
                    }
                    return;
                }

                // 先获取所有压缩包中的文件数量
                int partFiles = 0;
                for (File part : waitValidateFiles) {
                    try {
                        ZipFile zipFile = new ZipFile(part);
                        Enumeration<? extends ZipEntry> entries = zipFile.entries();
                        // 计算当前分卷内的文件数
                        while (entries.hasMoreElements()) {
                            entries.nextElement();
                            partFiles++;
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                // 验证分卷文件（占进度5%）
                int checkFiles = 0;
                for (File part : waitValidateFiles) {
                    try {
                        ZipFile zipFile = new ZipFile(part);
                        Enumeration<? extends ZipEntry> entries = zipFile.entries();
                        while (entries.hasMoreElements()) {
                            checkFiles++;
                            ZipEntry entry = entries.nextElement();
                            String fileName = entry.getName();

                            if (!crcMap.containsKey(fileName)) {
                                errors.add("多余文件: " + fileName + " 在 " + part.getName());
                                continue;
                            }

                            long expected = crcMap.get(fileName);
                            long actual = entry.getCrc();
                            if (actual != expected) {
                                errors.add(String.format("CRC校验失败: %s (预期:%08X 实际:%08X)", fileName, expected, actual));
                            }
                            crcMap.remove(fileName);

                            // 进度
                            currentStep = 95 + (checkFiles * 5) / partFiles;
                            if (callback != null) {
                                callback.onValidationProgress(currentStep);
                            }
                        }
                        zipFile.close();
                    } catch (ZipException e) {
                        errors.add("损坏的分卷: " + part.getName());
                    } catch (IOException e) {
                        errors.add("无法读取: " + part.getName());
                    } catch (NullPointerException e) {
                        errors.add("程序本身空指针: " + part.getName());
                    } catch (Exception e) {
                        errors.add("未知错误: " + part.getName());
                    }
                }

                // 检查缺失文件
                for (Map.Entry<String, Long> entry : crcMap.entrySet()) {
                    if (entry.getValue() != null) {
                        errors.add("文件缺失: " + entry.getKey());
                    }
                }
                if (callback != null) {
                    callback.onValidationComplete(errors);
                }
            }
        }).start();
    }

    public interface ZipProgressListener {
        void onProgress(int percent);
        void onComplete(List<File> zipFiles);
        void onError(Throwable throwable);
        void onCancel();
    }

    public interface ValidationCallback {
        // 验证进度 (0-100)
        void onValidationProgress(int percent);
        // 验证完成（返回错误列表）
        void onValidationComplete(List<String> errors);
    }
}